use crate::action_factory::{ActionTrait, ActionData};
use log::*;
use simple_error::*;
use std::net::{TcpStream, SocketAddr};
use std::time::Duration;
use native_tls::{TlsConnectorBuilder,TlsConnector};
use std::str::FromStr;
use std::ops::Sub;

pub struct ActionTcp {
    task:ActionData
}

impl ActionTcp {
    fn executor_tcp(&mut self) {
        let task_conf = &self.task.task_action;
        let sockAddr = SocketAddr::from_str(task_conf.target.as_str()).unwrap();
        let startTime = chrono::Utc::now().time();
        let r = TcpStream::connect_timeout(&sockAddr,
                                           Duration::from_millis(task_conf.timeout as u64));

        match r {
            Ok(stream) => {
                if task_conf.output_result {
                    info!("tcp {} connect success,time:{}ms...",task_conf.target,(chrono::Utc::now().time() - startTime).num_milliseconds());
                }

                self.task.do_recover();
            }
            Err(e) => {
                error!("tcp {} connect error:{}",task_conf.target,e);
                self.task.do_failed();
            }
        }

    }

    fn executor_tcpTls(&mut self) {
        let task_conf = &self.task.task_action;
        let mut r = TlsConnector::builder();
        if task_conf.skip_tls_check {
            r.danger_accept_invalid_certs(true).
                danger_accept_invalid_hostnames(true);
        }

        let tHost = task_conf.target.split(":").collect::<Vec<&str>>();
        if tHost.len() < 2 {
            error!("target format error! {}",task_conf.target);
            return;
        }

        let tHost = tHost[0];
        let startTime = chrono::Utc::now().time();
        let connector = r.build().unwrap();
        let sockAddr = SocketAddr::from_str(task_conf.target.as_str()).unwrap();
        let rt = TcpStream::connect_timeout(&sockAddr,
                                                Duration::from_millis(task_conf.timeout as u64));

        match rt {
            Ok(stream) => {
                let mut stream = connector.connect(tHost,stream);
                match stream {
                    Ok(r) => {
                        if task_conf.output_result {
                            info!("tcp tls {} connect success,time:{}ms...",task_conf.target,(chrono::Utc::now().time() - startTime).num_milliseconds());
                        }

                        self.task.do_recover();
                    }
                    Err(e) => {
                        error!("tcp tls connect error:{}",e);
                        self.task.do_failed();
                    }
                }
            }
            Err(e) => {
                error!("tcp tls {} connect error:{}",task_conf.target,e);
                self.task.do_failed();
            }
        }

    }
}

impl ActionTrait for ActionTcp {
    fn id(&self) -> i32 {
        self.task.id
    }

    fn name(&self) -> &str {
        self.task.name.as_str()
    }

    fn append_data(&mut self, data: ActionData) {
        self.task = data;
    }

    fn executor(&mut self) {
        info!("start tcp or tcp_tls action:{} id:{}...",self.task.id,self.task.name);
        let task_conf = &self.task.task_action;
        match task_conf.atype.as_str() {
            "tcp" => {
                self.executor_tcp();
            },
            "tcp_tls" => {
                self.executor_tcpTls();
            }
            _ => {}
        }
    }
}

pub fn new_tcp(data: ActionData) -> ActionTcp {
    ActionTcp { task:data }
}